/******************************************************************************* * Copyright (c) 2004, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ant.internal.ui.editor.text; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.ant.internal.ui.editor.AntEditor; import org.eclipse.ant.internal.ui.model.AntElementNode; import org.eclipse.ant.internal.ui.model.AntModel; import org.eclipse.ant.internal.ui.model.AntProjectNode; import org.eclipse.ant.internal.ui.model.IAntElement; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.projection.ProjectionAnnotation; import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; public class AntFoldingStructureProvider { private AntEditor fEditor; private IDocument fDocument; /** * A mapping of the foldable position to the <code>AntElementNode<code> that represent that region */ private Map<Position, IAntElement> fPositionToElement = new HashMap<>(); public AntFoldingStructureProvider(AntEditor editor) { fEditor = editor; } private void updateFoldingRegions(ProjectionAnnotationModel model, Set<Position> currentRegions) { Annotation[] deletions = computeDifferences(model, currentRegions); Map<Annotation, Position> additionsMap = new HashMap<>(); for (Iterator<Position> iter = currentRegions.iterator(); iter.hasNext();) { Position position = iter.next(); IAntElement node = fPositionToElement.get(position); additionsMap.put(new ProjectionAnnotation(node.collapseProjection()), position); } if ((deletions.length != 0 || additionsMap.size() != 0)) { model.modifyAnnotations(deletions, additionsMap, new Annotation[] {}); } } private Annotation[] computeDifferences(ProjectionAnnotationModel model, Set<Position> additions) { List<Annotation> deletions = new ArrayList<>(); for (Iterator<Annotation> iter = model.getAnnotationIterator(); iter.hasNext();) { Object annotation = iter.next(); if (annotation instanceof ProjectionAnnotation) { Annotation annot = (Annotation) annotation; Position position = model.getPosition(annot); if (additions.contains(position)) { additions.remove(position); } else { deletions.add(annot); } } } return deletions.toArray(new Annotation[deletions.size()]); } public void updateFoldingRegions(AntModel antModel) { fPositionToElement = new HashMap<>(); try { ProjectionAnnotationModel model = fEditor.getAdapter(ProjectionAnnotationModel.class); if (model == null) { return; } Set<Position> currentRegions = new HashSet<>(); List<IAntElement> root = new ArrayList<>(); AntProjectNode node = antModel.getProjectNode(); if (node != null && node.getOffset() != -1) { root.add(node); List<AntElementNode> nodes = antModel.getNonStructuralNodes(); root.addAll(nodes); } addFoldingRegions(currentRegions, root); updateFoldingRegions(model, currentRegions); } catch (BadLocationException be) { // ignore as document has changed } } private void addFoldingRegions(Set<Position> regions, List<IAntElement> children) throws BadLocationException { // add a Position to 'regions' for each foldable region for (IAntElement element : children) { if (element.getImportNode() != null || element.isExternal()) { continue; // elements are not really in this document and therefore are not foldable } int startLine = fDocument.getLineOfOffset(element.getOffset()); int endLine = fDocument.getLineOfOffset(element.getOffset() + element.getLength()); if (startLine < endLine) { int start = fDocument.getLineOffset(startLine); int end = fDocument.getLineOffset(endLine) + fDocument.getLineLength(endLine); Position position = new Position(start, end - start); regions.add(position); fPositionToElement.put(position, element); } List<IAntElement> childNodes = element.getChildNodes(); if (childNodes != null) { addFoldingRegions(regions, childNodes); } } } public void setDocument(IDocument document) { fDocument = document; } }